﻿using zijian666.Data.Extensions;

namespace zijian666.Data;

/// <summary>
/// 用于自动打开/关闭数据库连接
/// </summary>
public record ConnectionScope : IEnumerable, IAsyncDisposable, IDisposable
{
    private readonly ConnectionScope? _parent;
    private readonly List<object> _components = [];
    private readonly long _timestamp = Stopwatch.GetTimestamp();
    /// <summary>
    /// 
    /// </summary>
    /// <param name="connection"></param>
    /// <exception cref="NullReferenceException" />
    public ConnectionScope(IDbConnection connection, bool dispose = false)
    {
        ArgumentNullException.ThrowIfNull(connection);
        Connection = connection;
        _parent = EntryScope(this);
        _components.Add(() => ExitScope(this));
        if (IsRoot && connection.NotOpen())
        {
            _components.Add(connection);
            _connectionOnlyClose = !dispose;
        }
    }

    public bool IsRoot => _parent is null;

    public IDbConnection Connection { get; }
    public DbConnection DbConnection => (DbConnection)Connection;

    public TransactionScope Transaction => GetComponents<TransactionScope>().LastOrDefault();

    public IDbCommand Command => GetComponents<IDbCommand>().LastOrDefault();

    public IEnumerable<T> GetComponents<T>()
        => _components.OfType<T>();

    public long Timestamp => _timestamp;

    public void Dispose()
    {
        foreach (var compoent in _components)
        {
            compoent.ComponentDispose(_connectionOnlyClose);
        }
        _components.Clear();
        GC.SuppressFinalize(this);
    }

    public async ValueTask DisposeAsync()
    {
        foreach (var compoent in _components)
        {
            await compoent.ComponentDisposeAsync(_connectionOnlyClose);
        }
        _components.Clear();
        GC.SuppressFinalize(this);
    }

    public void Add(object component)
    {
        _components.Add(component);
    }

    IEnumerator IEnumerable.GetEnumerator()
        => _components.GetEnumerator();

    private ConnectionScope FindParent(ConnectionScope current)
    {
        var scope = current;
        while (scope is not null)
        {
            if (scope == this)
            {
                return _parent;
            }
            scope = scope._parent;
        }
        return null;
    }

    private readonly static AsyncLocal<LinkedList<ConnectionScope>> LOCAL_DATA = new();
    private readonly bool _connectionOnlyClose;

    public static LinkedList<ConnectionScope> Scope => LOCAL_DATA.Value ??= [];

    private static ConnectionScope EntryScope(ConnectionScope scope)
    {
        // 遍历链表 _items
        // 找到第一个 Connection 一样的节点, 替换并返回原节点
        // 如果没有找到, 则添加到链表末尾, 返回null
        var items = Scope;
        var node = items.First;
        while (node is not null)
        {
            if (node.Value.Connection == scope.Connection)
            {
                var parent = node.Value;
                node.Value = scope;
                return parent;
            }
            node = node.Next;
        }
        items.AddLast(scope);
        return null;
    }

    private static void ExitScope(ConnectionScope scope)
    {
        // 遍历链表 _items
        // 找到第一个 Connection 一样的节点, 如果 parent 不为空, 则替换为 parent, 否则移除
        // 如果没有找到, 则抛出异常
        var items = Scope;
        var node = items.First;
        while (node is not null)
        {
            if (node.Value.Connection == scope.Connection)
            {
                if (node.Value.IsRoot)
                {
                    items.Remove(node);
                }
                else
                {
                    var parent = scope.FindParent(node.Value);
                    if (parent is not null)
                    {
                        node.Value = parent;
                    }
                }
                return;
            }
            node = node.Next;
        }
    }
}